package org.netty.demo.lihh.notice.util;

import cn.hutool.core.util.StrUtil;
import io.netty.channel.Channel;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 表示 通道事件
 * 以 特殊的结构，保存 角色 tk 消息通道关系
 *
 * @author lihh
 */
public class ChannelHandler {
  // channel 映射 cache
  private static final Map<String, Channel> channelMappingCache = new HashMap<>();
  /* 空间 转换 时间 */
  // 表示 channelId 以及 tk的映射
  private static final Map<String, String> channelIdAndTkMapping = new HashMap<>();
  // 表示 tk 以及 roleId 的映射
  private static final Map<String, String> tkAndRoleIdMapping = new HashMap<>();
  // 构建 通道群组
  // Map<角色id, Map<tk, channelId>>
  public static final Map<String, Map<String, String>> channelGroup = new HashMap<>();
  
  /**
   * 添加 channel 集合
   *
   * @param channel socket channel
   * @return 返回映射的 channelId
   * @author lihh
   */
  public static String addChannel(Channel channel) {
    return addChannel(channel, StrUtil.uuid());
  }
  
  /**
   * 根据 socket channel 拿到用户tk
   *
   * @author lihh
   * @param channel socket channel
   * @return return tk
   */
  public static String getTkByChannel(Channel channel) {
    for (Map.Entry<String, Channel> map : channelMappingCache.entrySet())
      if (channel == map.getValue())
        return channelIdAndTkMapping.get(map.getKey());
    
    return null;
  }
  
  /**
   * 根据 channel 拿到 channelId
   *
   * @param channel socket channel 通道
   * @author lihh
   */
  public static String getChannelId(Channel channel) {
    if (!channelMappingCache.containsValue(channel)) return null;
    
    for (Map.Entry<String, Channel> map : channelMappingCache.entrySet())
      if (map.getValue() == channel) return map.getKey();
    
    return null;
  }
  public static String addChannel(Channel channel, String channelId) {
    channelMappingCache.put(channelId, channel);
    return channelId;
  }
  
  /**
   * 添加 channel 集合
   *
   * @param roleId    角色id
   * @param tk        用户tk
   * @param channelId socket channel id
   * @return return channelId
   * @author lihh
   */
  public static String addChannel(String roleId, String tk, String channelId) {
    if (!channelMappingCache.containsKey(channelId)) return null;
    
    Map<String, String> tkCache = channelGroup.computeIfAbsent(roleId, k -> new HashMap<>());
    tkCache.put(tk, channelId);
    
    channelIdAndTkMapping.put(channelId, tk);
    tkAndRoleIdMapping.put(tk, roleId);
    return channelId;
  }
  
  /**
   * 根据 channelId 获取 通道
   *
   * @param channelId 通道id
   * @return 返回 socket channel
   * @author lihh
   */
  public static Channel getChannel(String channelId) {
    return channelMappingCache.get(channelId);
  }
  
  /**
   * 根据 角色id 以及 tk 拿到 Channel
   *
   * @param roleId 角色id
   * @param tk     用户 tk
   * @return 返回 socket channel
   * @author lihh
   */
  public static Channel getChannel(String roleId, String tk) {
    Map<String, String> channelMapping = channelGroup.getOrDefault(roleId, new HashMap<>());
    String channelId = channelMapping.get(tk);
    
    if (channelId == null) return null;
    return channelMappingCache.get(channelId);
  }
  
  /**
   * 根据 key 拿到 value
   *
   * @param value value 值
   * @param map   map集合
   * @return 返回 value
   * @author lihh
   */
  private static String getKeyByValue(String value, Map<String, String> map) {
    if (null == map || map.isEmpty()) return null;
    
    for (Map.Entry<String, String> curr : map.entrySet())
      if (StrUtil.equals(value, curr.getValue())) return curr.getKey();
    return null;
  }
  
  /**
   * remove socket channel
   *
   * @param tk user
   * @author lihh
   */
  public static void removeChannel(String tk) {
    String channelId = getKeyByValue(tk, channelIdAndTkMapping);
    if (StrUtil.isEmpty(channelId)) return;
    
    channelIdAndTkMapping.remove(channelId);
    channelMappingCache.remove(channelId);
    
    String roleId = tkAndRoleIdMapping.get(tk);
    tkAndRoleIdMapping.remove(tk);
    
    channelGroup.getOrDefault(roleId, new HashMap<>()).remove(tk);
  }
  
  /**
   * 根据 角色id 删除 channel
   *
   * @param roleId 角色id
   * @author lihh
   */
  public static void removeChannelByRoleId(String roleId) {
    // 根据 roleId 拿到所有的 tk
    ArrayList<String> tkList = new ArrayList<>(channelGroup.getOrDefault(roleId, new HashMap<>()).keySet());
    if (tkList.isEmpty())
      return;
    
    tkList.forEach(ChannelHandler::removeChannel);
  }
  
  /**
   * 排除指定的channelId，获取同一个角色下的其他的Channel
   *
   * @param roleId            角色id
   * @param excludeChannelIds 排除 通道id
   * @return 返回通道集合
   * @author lihh
   */
  public static List<Channel> getChannelExcludeChannelId(String roleId, List<String> excludeChannelIds) {
    Map<String, String> channelMap = channelGroup.get(roleId);
    if (null == channelMap) return Collections.emptyList();
    
    // 如果执行到这里的话，说明起码是有角色的
    List<String> includeChannelIds = channelMap
        .values()
        .stream()
        .filter(currChannelId -> !excludeChannelIds.contains(currChannelId))
        .collect(Collectors.toList());
    
    return includeChannelIds
        .stream()
        .map(channelMappingCache::get)
        .filter(Objects::nonNull)
        .collect(Collectors.toList());
  }
}
